package last.project;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Random;

import enemies.*;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AbsoluteLayout;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import gameView.*;

public class GameScreen extends Activity implements View.OnTouchListener {
	
	public ArrayList<Ball> Enemies, Defended, Guards;
	private Paint EnemiesPaint, DefendedPaint, GuardsPaint;
	private Context cxt;
	public GameDrawView GameView;
	private ArrayList<MotionEvent> previousEvents;
	private static int MAX_X, MAX_Y;
	
	public int Score=0;
	public boolean gameOver=false;
	public double Difficulty=3.0;
	
	private ArrayList<Integer> timerValues, timerMaxes, timerStarts;
	private ArrayList<Double> enemyDifficultyRequirements;
	private int time=0; //in millis
	public static final int TIMER_INCREMENT=50;
	public Thread timerThread;
	public Handler glove = new Handler() {
		@Override
		public void handleMessage(Message msg) {
		}
	};
	private boolean isRunning;
	
	private TextView timeText, scoreText;
	
	//initializing
	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);
        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        cxt=this;
        setContentView(R.layout.gamelayout);
        
        setupVars();
        setupLayout();
	}
	@SuppressWarnings("deprecation")
	private void setupLayout() {
		AbsoluteLayout layout = (AbsoluteLayout) findViewById(R.id.AbsoluteLayout1);
		AbsoluteLayout.LayoutParams params;
		
		params = new AbsoluteLayout.LayoutParams(MAX_X, MAX_Y, 0, 0);
        GameView = new GameDrawView(cxt, this);
		layout.addView(GameView, params);
		
		params = new AbsoluteLayout.LayoutParams(MAX_X/4, 30, 0, 0);
		timeText = new TextView(cxt);
		timeText.setGravity(Gravity.LEFT);
		timeText.setTextSize(12);
		layout.addView(timeText, params);
		
		params = new AbsoluteLayout.LayoutParams(MAX_X/4, 30, 3*MAX_X/8, 0);
		scoreText = new TextView(cxt);
		scoreText.setGravity(Gravity.CENTER_HORIZONTAL);
		scoreText.setText("0");
		scoreText.setTextSize(12);
		layout.addView(scoreText, params);
		

		GameView.setOnTouchListener(this);
	}
	private void setupVars() {
        MAX_X=FinalProjectActivity.MAX_X;
        MAX_Y=FinalProjectActivity.MAX_Y;
        
        setupTimers();
        
        previousEvents = new ArrayList<MotionEvent>();
        
        enemyDifficultyRequirements = new ArrayList<Double>();
        enemyDifficultyRequirements.add(0.0);
        enemyDifficultyRequirements.add(2.0);
		
		EnemiesPaint = new Paint();
		EnemiesPaint.setColor(Color.RED);
		EnemiesPaint.setAntiAlias(true);
		DefendedPaint = new Paint();
		DefendedPaint.setColor(Color.GREEN);
		DefendedPaint.setAntiAlias(true);
		GuardsPaint = new Paint();
		GuardsPaint.setColor(Color.WHITE);
		GuardsPaint.setAntiAlias(true);
		
		Enemies = new ArrayList<Ball>();
		Defended = new ArrayList<Ball>();
		Guards = new ArrayList<Ball>();
		
		
		initBalls();
    }
	private void setupTimers() {
		timerMaxes=new ArrayList<Integer>();
        timerMaxes.add(5);
        timerMaxes.add(10);
        
		timerStarts = new ArrayList<Integer>();
		timerStarts.add(0);
		timerStarts.add(30000);
        
		timerValues = new ArrayList<Integer>();
        for (int i=0 ;i<timerMaxes.size(); i++) {
        	timerValues.add(timerMaxes.get(i));
        }
        
    	time=0;
    	timerThread = new Thread(new Runnable(){

			public void run() {
				try {
					if (!isRunning) {
						
					}
					for (int i=0; i>=0; i++) {
						if (isRunning) {
							Thread.sleep(TIMER_INCREMENT);
							addBalls();
						}
					}
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
    		
    	});
    	isRunning=true;
    	timerThread.start();
	}
	private void initBalls() {
		Ball orb = new Ball(cxt, GameView, MAX_X/2, MAX_Y/4, 15);
		Defended.add(orb);
		Ball guard = new Ball(cxt, GameView, MAX_X/4, MAX_Y*3/4, 30);
		Guards.add(guard);/**/
		/*Ball e1 = new Ball(cxt, GameView, 100, 100, 30);
		Ball e2 = new Ball(cxt, GameView, 350, 350, 30);
		e1.setXV(0);
		e1.setYV(0);
		e2.setXV(-5);
		e2.setYV(-5);
		Enemies.add(e1);
		Enemies.add(e2);*/
	}

	
	//recurring calculations
	public void redraw(Canvas canvas) {
		if (gameOver)
			return;
		
		String timeString="";
		timeString+=time/1000;
		timeString=timeString+".";
		timeString=timeString+(time/10)%100;
		timeText.setText(timeString);/**/
		
		while (previousEvents.size()>0) {
			processMotionEvent(previousEvents.get(0));
			previousEvents.remove(0);
		}
		
		for (int i=0; i<Enemies.size(); i++) 
			Enemies.get(i).step();
		for (int i=0; i<Defended.size(); i++) 
			Defended.get(i).step();
		for (int i=0; i<Guards.size(); i++) 
			Guards.get(i).step();
		
		//addBalls();
		
		detectCollisions();
		
		canvas.drawColor(Color.BLUE);
		for (int i=0 ;i<Enemies.size();i ++) 
			canvas.drawCircle((float)Enemies.get(i).getX(), (float)Enemies.get(i).getY(), (float)Enemies.get(i).getRadius(), EnemiesPaint);
		for (int i=0 ;i<Guards.size();i ++) 
			canvas.drawCircle((float)Guards.get(i).getX(), (float)Guards.get(i).getY(), (float)Guards.get(i).getRadius(), GuardsPaint);
		for (int i=0 ;i<Defended.size();i ++) 
			canvas.drawCircle((float)Defended.get(i).getX(), (float)Defended.get(i).getY(), (float)Defended.get(i).getRadius(), DefendedPaint);
		
		decellerate();
	}
	private void addBalls() {
		time+=TIMER_INCREMENT;
		timerValues.set(0, timerValues.get(0)-TIMER_INCREMENT);
		if (timerValues.get(0)<=0) {
			Ball e = new BasicEnemy(cxt, GameView, 6, 10);
			addNewEnemy(e);
			timerMaxes.set(0, (int)((2.0*40000.0/((double)time+40000.0))*1000.0)+500);
			timerValues.set(0, timerMaxes.get(0));
		}
		if (time>=timerStarts.get(1)) {// && Difficulty>=enemyDifficultyRequirements.get(1)) {
			timerValues.set(1, timerValues.get(1)-TIMER_INCREMENT);
			if (timerValues.get(1)<=0) {
				Random rand = new Random();
				Ball e = new SlowTracker(cxt, GameView, Defended.get(rand.nextInt(Defended.size())), 4, 8);
				addNewEnemy(e);
				timerMaxes.set(1, (int)((6.0*40000.0/((double)time-30000.0+40000.0))*1000.0));
				timerValues.set(1, timerMaxes.get(1));
			}
		}
	}	
	private void addNewEnemy(Ball e) {
		double radius=10, x, y;
		double placeSpot = Math.random()*(2*MAX_X+2*MAX_Y-8*radius);
		if (placeSpot<=MAX_X-2*radius) {
			x=placeSpot+radius;
			y=radius;
		} else if (placeSpot<=2*MAX_X-4*radius) {
			x=placeSpot-MAX_X+3*radius;
			y=MAX_Y-radius;
		} else if (placeSpot<=2*MAX_X+MAX_Y-6*radius) {
			x=radius;
			y=placeSpot-2*MAX_X+5*radius;
		} else {
			x=MAX_X-radius;
			y=placeSpot-2*MAX_X-MAX_Y+7*radius;
		}
		
		e.setX(x);
		e.setY(y);
		
		Enemies.add(e);
	}
	private void decellerate() {
		double DEFENDED_RESIST_FACTOR=.92,
			   GUARDS_RESIST_FACTOR=.98,
			   DEFENDED_FRICTION=.05,
			   GUARDS_FRICTION=.01;
		
		
		double totalMagnitude, tMagnitude;
		for (int i=0; i<Defended.size(); i++) {
			totalMagnitude=distance(0,0, Defended.get(i).getXV(), Defended.get(i).getYV());
			if (totalMagnitude<DEFENDED_FRICTION) {
				Defended.get(i).setXV(0);
				Defended.get(i).setYV(0);
			} else {
				tMagnitude=totalMagnitude-DEFENDED_FRICTION;
				Defended.get(i).setXV(Defended.get(i).getXV()*(tMagnitude/totalMagnitude));
				Defended.get(i).setYV(Defended.get(i).getYV()*(tMagnitude/totalMagnitude));
			}
			Defended.get(i).setXV(Defended.get(i).getXV()*DEFENDED_RESIST_FACTOR);
			Defended.get(i).setYV(Defended.get(i).getYV()*DEFENDED_RESIST_FACTOR);
		}
		for (int i=0; i<Guards.size(); i++) {
			totalMagnitude=distance(0,0, Guards.get(i).getXV(), Guards.get(i).getYV());
			if (totalMagnitude<GUARDS_FRICTION) {
				Guards.get(i).setXV(0);
				Guards.get(i).setYV(0);
			} else {
				tMagnitude=totalMagnitude-GUARDS_FRICTION;
				Guards.get(i).setXV(Guards.get(i).getXV()*(tMagnitude/totalMagnitude));
				Guards.get(i).setYV(Guards.get(i).getYV()*(tMagnitude/totalMagnitude));
			}
			Guards.get(i).setXV(Guards.get(i).getXV()*GUARDS_RESIST_FACTOR);
			Guards.get(i).setYV(Guards.get(i).getYV()*GUARDS_RESIST_FACTOR);
		}
	}
	private void detectCollisions() {
		//Enemy balls hitting friendly balls
		for (int i=0; i<Enemies.size(); i++) {
			Ball e;
			e=Enemies.get(i);
			for (Ball d : Defended) {
				if (overlap(e, d)) {
					gameOver(false);
				}
			}
			for (Ball g : Guards) {
				if (overlap(e, g)) {
					Enemies.remove(i);
					i--;
					enemyHit();
				}
			}
		}
		
		for (int i=0 ;i<Enemies.size(); i++) {
			for (int j=i+1 ;j<Enemies.size(); j++) {
				if (overlap(Enemies.get(i), Enemies.get(j))) {
					ballBounce(Enemies.get(i), Enemies.get(j));
				}
			}
		}
		for (int i=0 ;i<Defended.size(); i++) {
			for (int j=i+1; j<Defended.size(); j++) {
				if (overlap(Defended.get(i), Defended.get(j))) {
					ballBounce(Defended.get(i), Defended.get(j));
				}
			}
		}
		for (int i=0 ;i<Guards.size(); i++) {
			for (int j=i+1; j<Guards.size(); j++) {
				if (overlap(Guards.get(i), Guards.get(j))) {
					ballBounce(Guards.get(i), Guards.get(j));
				}
			}
		}
		for (int i=0; i<Guards.size(); i++) {
			for (int j=0; j<Defended.size(); j++) {
				if (overlap(Guards.get(i), Defended.get(j))) {
					ballBounce(Guards.get(i), Defended.get(j));
				}
			}
		}
		
		for (Ball enemy : Enemies) 
			wallBounce(enemy);
		for (Ball defended : Defended) 
			wallBounce(defended);
		for (Ball guard : Guards) 
			wallBounce(guard);
	}
	public boolean onTouch(View v, MotionEvent event) {
		previousEvents.add(event);
		/**/
		return true;
	}
	private void processMotionEvent(MotionEvent event) {
		double touchX, touchY;
		touchX=event.getX();
		touchY=event.getY();
		for (int i=0; i<Defended.size(); i++) 
			affectByGravity(Defended.get(i), touchX, touchY, true);
		for (int i=0 ;i<Guards.size(); i++) 
			affectByGravity(Guards.get(i), touchX, touchY, false);
	}
	
	//end and score
	public void gameOver(boolean win) {
		if (gameOver)
			return;
		gameOver=true;
		timerThread.stop();
		
		ArrayList<Integer> Scores = FinalProjectActivity.HSSystem.Scores;
		int Names_Stored = FinalProjectActivity.HSSystem.Names_Stored;
		boolean newHighScore=false;
		
		if (Scores.size()<Names_Stored || (Scores.size()!=0 && Score>Scores.get(Scores.size()-1))) {
			newHighScore=true;
		}
		
		Intent tent = new Intent(cxt, GameOver.class);
		tent.putExtra("newHighScore", newHighScore);
		tent.putExtra("win", win);
		tent.putExtra("Score", Score);
		cxt.startActivity(tent);
	}
	public void enemyHit() {
		Score++;
		scoreText.setText(""+Score);
	}

	
	
	
	//calculation methods
	private boolean overlap(Ball ball1, Ball ball2) {
		return (distance(ball1.getX(), ball1.getY(), ball2.getX(), ball2.getY())<ball1.getRadius()+ball2.getRadius());
	}
	private boolean wallBounce(Ball b) {
		//System.out.println("Radius:"+(int)b.getRadius()+"\tXV:"+(int)b.getXV()+"\tYV:"+(int)b.getYV());
		double excessPartOfMove;
		if (b.getY()+b.getRadius()>=MAX_Y) {
			excessPartOfMove=(b.getY()-MAX_Y+b.getRadius())/(b.getYV());
			b.setY(MAX_Y-b.getRadius()-.01);
			b.setX(b.getX()-excessPartOfMove*b.getXV());
			b.setYV(bounceVelocity(b.getYV(), false));
			return true;
		} else if (b.getY()-b.getRadius()<=0) {
			excessPartOfMove=(b.getRadius()-b.getY())/(b.getYV());
			b.setY(b.getRadius()+.01);
			//b.setX(b.getX()-excessPartOfMove*b.getXV());
			b.setYV(bounceVelocity(b.getYV(), true));
			return true;
		} 
		if (b.getX()+b.getRadius()>=MAX_X) {
			excessPartOfMove=(b.getX()-MAX_X+b.getRadius())/(b.getXV());
			b.setX(MAX_X-b.getRadius()-.01);
			b.setY(b.getY()-excessPartOfMove*b.getYV());
			b.setXV(bounceVelocity(b.getXV(), false));
			return true;
		} else if (b.getX()-b.getRadius()<=0) {
			excessPartOfMove=(b.getRadius()-b.getX())/(b.getXV());
			b.setX(b.getRadius()+.01);
			b.setY(b.getY()-excessPartOfMove*b.getYV());
			b.setXV(bounceVelocity(b.getXV(), true));
			return true;
		}
		return false;
	}
	private boolean ballBounce(Ball ball1, Ball ball2) {
		double X=(ball2.getX()-ball2.getXV()) - (ball1.getX()-ball1.getXV()), 
			   Y=(ball2.getY()-ball2.getYV()) - (ball1.getY()-ball1.getYV()), 
			   XV=ball2.getXV()-ball1.getXV(), 
			   YV=ball2.getYV()-ball1.getYV(),
			   dist=ball1.getRadius()+ball2.getRadius();
		double A=XV*XV+YV*YV,
			   B=2*(X*XV+Y*YV),
			   C=X*X+Y*Y-dist*dist;
		double t=(-B-Math.pow(B*B-4*A*C, .5))/(2*A);
		if (t<0)
			System.out.println("(t:"+t+")(X:"+X+")(Y:"+Y+")(XV:"+XV+")(YV:"+YV+")(dist:"+dist+")");
		
		ball1.setX(ball1.getX()-ball1.getXV()*(1.0-t));
		ball1.setY(ball1.getY()-ball1.getYV()*(1.0-t));
		ball2.setX(ball2.getX()-ball2.getXV()*(1.0-t));
		ball2.setY(ball2.getY()-ball2.getYV()*(1.0-t));/**/
		
		double mass1=Math.pow(ball1.getRadius(), 2),
			   mass2=Math.pow(ball2.getRadius(), 2);
		
		Line tangeant = (new Line(X, Y)).perpendicularTo();//check
		Line movement = new Line(XV, YV);//check
		Line glance = new Line(tangeant.angle(), Math.abs(movement.magnitude()*Math.cos(tangeant.angle()-movement.angle())), 0);//check
		Line impact = new Line(tangeant.perpendicularTo().angle(), Math.abs(movement.magnitude()*Math.sin(tangeant.angle()-movement.angle())), 0);//check
		Line rebounce1 = new Line(tangeant.perpendicularTo().angle(), 2*impact.magnitude()*(mass2)/(mass1+mass2), 0);
		Line rebounce2 = new Line(tangeant.perpendicularTo().angle(), Math.abs(impact.magnitude()*(mass1-mass2)/(mass1+mass2)), 0);
		
		glance.checkSign(movement);
		rebounce1.checkSign(movement);
		if (mass1>mass2) {
			rebounce2.checkSign(movement.reverse());
		} else {
			rebounce2.checkSign(movement);
		}
		
		ball2.setXV(ball1.getXV()+rebounce2.dX+glance.dX);
		ball2.setYV(ball1.getYV()+rebounce2.dY+glance.dY);
		ball1.setXV(ball1.getXV()+rebounce1.dX);
		ball1.setYV(ball1.getYV()+rebounce1.dY);
		
		/*double avgXV=(ball1.getXV()*mass1+ball2.getXV()*mass2)/(mass1+mass2), 
			   avgYV=(ball1.getYV()*mass1+ball2.getYV()*mass2)/(mass1+mass2);
		
		ball1.setXV(2*avgXV-ball1.getXV());
		ball1.setYV(2*avgYV-ball1.getYV());
		ball2.setXV(2*avgXV-ball2.getXV());
		ball2.setYV(2*avgYV-ball2.getYV());*/
		
		
		return false;
	}
	private void affectByGravity(Ball ball, double sourcex, double sourcey, boolean isAttract) {
		double magnitudeMultiplier=0;
		if (!isAttract) 
			magnitudeMultiplier=2.5;
		else
			magnitudeMultiplier=-1.5;
		/*
		 * must change magnitudeMultipler depending on isAttract
		 */
		double magnitude, dist;
		dist=distance(ball.getX(), ball.getY(), sourcex, sourcey);
		magnitude=calcMagnitude(dist);
		ball.setXV(ball.getXV()+(ball.getX()-sourcex)/dist*magnitude*magnitudeMultiplier);
		ball.setYV(ball.getYV()+(ball.getY()-sourcey)/dist*magnitude*magnitudeMultiplier);
	}
	private double distance(double x1, double y1, double x2, double y2) {
		double c2 = (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1);
		return Math.pow(c2, 1.0/2.0);
	}
	private double calcMagnitude(double x) {
		//1.5/(1+1.05^(x-150))+4*10000*(x+40)^(-2.5)
		return 1.5/(1.0+Math.pow(1.05, x-150))+40000*Math.pow(x+40, -2.5);
	}
	private double bounceVelocity(double initV, boolean toPositive) {
		if (toPositive && initV<0) {
			return initV*-1;
		} else if (toPositive) {
			return initV*1;
		} else if (initV<0) {
			return initV*1;
		} else {
			return initV*-1;
		}
	}
}
